package reader;

import java.util.ArrayList;
import java.util.List;

import models.asl.Edge;
import models.asl.EdgeType;
import models.asl.Graph;
import models.asl.Node;
import models.asl.NodeType;

//import org.apache.log4j.Logger;


/**
 * Class for Transform the file content list into Graph
 * @author Tiancheng Zhang
 *
 */
public class Transformation {

//      private static final Logger logger = Logger.getLogger(Transformation.class);
        
        /**asl file reader*/
        private ASLFileReader aslFileReader;
        
        /**List of Graphs*/
        private List<String> fileContent;
        private Graph domainGraph;
        boolean []processed;
        private List<Graph> graphList;
        
        /**
         * Transformation Constructor
         */
        public Transformation(String filePath)
        {
                aslFileReader = new ASLFileReader(filePath);
                domainGraph = new Graph();
                graphList = new ArrayList<Graph>();
        }
        
        /**
         *Generate the Graph
         */
        public List<Graph> generate() throws Exception
        {
                //read the file
                aslFileReader.readFile();
                fileContent = aslFileReader.getFileContent();
                
                processed = new boolean[fileContent.size()];
                //initialization
                for(int i = 0; i < fileContent.size(); i++)
                {
                        processed[i] = false;
                }
                

                        
                int triEventIndex = findNextEntry();
                
                //has trigging event
                while(triEventIndex != -1)
                {
//                      logger.info("Start of Create Graph For Trigging event:"+ fileContent.get(triEventIndex));
                        //mark the TE as processed
//                      processed[triEventIndex] = true;
                        domainGraph = new Graph();
                        
                        //Create a start node
                        Node startNode = new Node(-1, fileContent.get(triEventIndex), NodeType.TRIGGINGEVENT, false, new ArrayList<Edge>(), new ArrayList<Edge>());
                        domainGraph.getNodes().add(startNode);
//                      logger.info("TR EVENT: "+fileContent.get(triEventIndex)+" is found");
                        
                        eachTEDomainGraphGeneration(triEventIndex, -1);
                        
                        graphList.add(domainGraph);
                        
//                      logger.info("End of Create Graph For Trigging event:"+ fileContent.get(triEventIndex));
                        triEventIndex = findNextEntry();
                }
                return graphList;
        }
        
        /**
         * Generate graph for each trigging event
         * @param entry trigging event index
         */
        private void eachTEDomainGraphGeneration(int entry, int prevNodeId) throws Exception
        {
                //for each plan
                do
                {
                        processed[entry] = true;
                        //consider entry type
                        Node planNode = new Node(entry, fileContent.get(entry), NodeType.PLAN, false, new ArrayList<Edge>(), new ArrayList<Edge>());
                        //setup the link
                        if(this.domainGraph.findNodeById(prevNodeId) != null)
                        {
                                this.domainGraph.findNodeById(prevNodeId).getOutgoingEdges().add(new Edge(prevNodeId,entry, EdgeType.ALTPLANEDGE));
                                planNode.getInComingEdges().add(new Edge(prevNodeId,entry, EdgeType.ALTPLANEDGE));
                        }
                        else
                        {
//                              logger.info("Cannot Find the previous Node with id:" + prevNodeId);
                                throw new Exception("Cannot Find the Node with id:"+prevNodeId);
                        }
                        //add plan Node to Graph
                        this.domainGraph.getNodes().add(planNode);
//                      logger.info("PLAN Node: "+domainGraph.findNodeById(entry).getName()+" added");
                        //traverse the plan
                        for(int i = entry + 1; i < fileContent.size(); i++)
                        {
                                //is not process && is (action/subgoal/context condition)
                                if(processed[i] == false && (fileContent.get(i).charAt(0) == '<' || fileContent.get(i).charAt(0) == ':'))
                                {
                                        if(fileContent.get(i).contains("!")&&fileContent.get(i).charAt(0) == '<')//subgoal
                                        {
                                                Node subgoalNode = new Node(i, fileContent.get(i).substring(2), NodeType.SUBGOAL,
                                                                                                        false, new ArrayList<Edge>(), new ArrayList<Edge>());
                                                //previous node index i - 1, set up link
                                                if(domainGraph.findNodeById(i-1) != null)
                                                {
                                                        domainGraph.findNodeById(i-1).getOutgoingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
                                                        subgoalNode.getInComingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
                                                }
                                                else
                                                {
//                                                      logger.info("Cannot Find the previous Node with id:" + (i-1));
                                                        throw new Exception("Cannot Find the Node with id:"+(i-1));
                                                }
                                                domainGraph.getNodes().add(subgoalNode);
                                                this.processed[i] = true;
//                                              logger.info("SUBGOAL Node: "+domainGraph.findNodeById(i).getName()+" added");
                                                //recursion
                                                while(getAlternativePlan(fileContent.get(i).substring(2)) != -1)
                                                {
                                                        eachTEDomainGraphGeneration(getAlternativePlan(fileContent.get(i).substring(2)), i);
                                                }
                                                
                                                
                                        }
                                        else if(fileContent.get(i).charAt(0) == '<')//action
                                        {       
                                                Node actionNode = new Node(i, fileContent.get(i).substring(2), NodeType.ACTION,
                                                                                                   false, new ArrayList<Edge>(), new ArrayList<Edge>());
                                                //previous node index i - 1, set up link
                                                if(domainGraph.findNodeById(i-1) != null)
                                                {
                                                        domainGraph.findNodeById(i-1).getOutgoingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
                                                        actionNode.getInComingEdges().add(new Edge(i-1, i , EdgeType.SEQUENCIALEDGE));
                                                }
                                                else
                                                {
//                                                      logger.info("Cannot Find the previous Node with id:" + (i-1));
                                                        throw new Exception("Cannot Find the Node with id:"+(i-1));
                                                }
                                                domainGraph.getNodes().add(actionNode);
                                                this.processed[i] = true;
//                                              logger.info("ACTION Node: "+domainGraph.findNodeById(i).getName()+" added");
                                        }
                                        else if(fileContent.get(i).charAt(0) == ':')
                                        {
                                                Node contextConNode = new Node(i, fileContent.get(i).substring(1), NodeType.CONTEXT_CONDITION,
                                                                                                           false, new ArrayList<Edge>(), new ArrayList<Edge>());
                                                //previous node index i - 1, set up link
                                                if(domainGraph.findNodeById(i-1) != null)
                                                {
                                                        domainGraph.findNodeById(i-1).getOutgoingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
                                                        contextConNode.getInComingEdges().add(new Edge(i-1, i , EdgeType.SEQUENCIALEDGE));
                                                }
                                                else
                                                {
//                                                      logger.info("Cannot Find the previous Node with id:" + (i-1));
                                                        throw new Exception("Cannot Find the Node with id:"+(i-1));
                                                }
                                                domainGraph.getNodes().add(contextConNode);
                                                this.processed[i] = true;
//                                              logger.info("CONTECT CON Node: "+domainGraph.findNodeById(i).getName()+" added");
                                        }//end else
                                }
                                else
                                {
                                        break;
                                }//end else
                                
                          }//end for
                }while((entry = getAlternativePlan(fileContent.get(entry))) != -1);
        }
        
        /**
         * Find the entry
         * @return index (-1 not found)
         */
        private int findNextEntry()
        {
                int index = -1;
                for(int i = 0; i < fileContent.size(); i++)
                {
                        //an trigging event that has not been processed
                        if(fileContent.get(i).charAt(0) == '+' && processed[i] == false)
                        {
                                int j = 0;
                                for(j = 0; j < fileContent.size(); j++ )
                                {
                                        //actions. Check whether the possible trigging event exists in it
                                        if(fileContent.get(j).charAt(0) == '<')
                                        {
                                                if(fileContent.get(j).contains(fileContent.get(i).substring(1)))
                                                {
                                                        break;
                                                }
                                        }
                                }
                                //found trigging event
                                if(j >= fileContent.size())
                                {
                                        index = i;
                                        return index;
                                }       
                        }
                }
                return index;
        }
        
        /**
         * Alternative plan
         */
        private int getAlternativePlan(String plan)
        {
                int index = -1;
                for(int i = 0; i < fileContent.size(); i++)
                {
                        if(processed[i] == false)
                        {
                                if(fileContent.get(i).contains(plan))
                                {
                                        index = i;
                                        return index;
                                }
                        }
                }
                return index;
        }
        
}